#include "RunManagement.h"
#include "MainWindow.h"

#include "simodo/shell/runner/Runner_interface.h"

#include <QFileInfo>
#include <QDockWidget>
#include <QComboBox>
#include <QCheckBox>
#include <QAction>
#include <QVBoxLayout>

#include <fstream>
#include <chrono>
#include <algorithm>

namespace shell  = simodo::shell;

RunManagement::RunManagement(MainWindow & main_window) 
    : _main_window(main_window)
{}

RunManagement::~RunManagement()
{
    stopModeling();
}

bool RunManagement::isRunnable(const QString & path)
{
    auto runner = _findRunner();

    if (runner == nullptr)
    {
        return false;
    }

    return runner->isRunnable(path);
}

bool RunManagement::canRunAutomatic()
{
    if (!_main_window.runners_combo()->isEnabled())
        return false;

    return _main_window.run_auto_check()->isChecked();
}

void RunManagement::updateRunnerCombo(const QString & file_name)
{
    QComboBox * combo = _main_window.runners_combo();

    if (!combo->currentText().isEmpty())
        _last_runner_name = combo->currentText();

    combo->clear();

    if (!file_name.isEmpty()) {
        QVector<shell::Runner_interface *> & runners = _main_window.runners();
        for(shell::Runner_interface * r : runners)
            if (r->isRunnable(file_name)) {
                combo->addItem(r->name());
                if (_last_runner_name == r->name()) {
                    int index = combo->count()-1;
                    combo->setCurrentIndex(index);

                    QCheckBox * check = _main_window.run_auto_check();
                    check->setChecked(r->isAutoRunActive());
                    check->setEnabled((r->plugin()->supports() & shell::PS_Run_AutoPreview) != 0);
                }
            }
    }

    combo->setEnabled(combo->count());
    _main_window.updateMenus();
}

void RunManagement::updateRunnerWidget(int /*index*/)
{
    if (_run_options_widget) {
        _main_window.run_options_layout()->removeWidget(_run_options_widget);
        _run_options_widget->hide();
        _run_options_widget = nullptr;
    }

    shell::Runner_interface * runner = _findRunner();
    if (!runner)
        return;

    _run_options_widget = runner->getToolbarWidget();

    if (_run_options_widget) {
        _main_window.run_options_layout()->addWidget(_run_options_widget);
        _run_options_widget->show();
    }

    QCheckBox * check = _main_window.run_auto_check();
    check->setChecked(runner->isAutoRunActive());
    check->setEnabled((runner->plugin()->supports() & shell::PS_Run_AutoPreview) != 0);
}

void RunManagement::checkAutoRun(bool checked)
{
    shell::Runner_interface * runner = _findRunner();

    if (runner == nullptr)
        return;

    runner->setAutoRunActive(checked);
}

bool RunManagement::startModeling(const QString &path)
{
    if (_state == shell::RunnerState::Paused && _last_runner)
        return _last_runner->startModeling(_last_file_name);

    auto runner = _findRunner();
    if (runner == nullptr)
        return false;

    _last_runner = runner;
    _last_file_name = path;
    return runner->startModeling(path);
}

bool RunManagement::stopModeling()
{
    auto runner = _findRunnerOrLast();
    if (runner == nullptr)
    {
        return false;
    }

    return runner->stopModeling();
}

bool RunManagement::pauseModeling()
{
    auto runner = _findRunnerOrLast();
    if (runner == nullptr)
    {
        return false;
    }

    return runner->pauseModeling();
}

void RunManagement::received(const QString & text)
{
    if (!text.startsWith('#')) _main_window.sendGlobal(text);
    else _main_window.executeVisualizationCommand(text);
}

void RunManagement::send(const QString & text)
{
    auto runner = _findRunnerOrLast();
    if (runner == nullptr)
    {
        return;
    }

    runner->send(text);
}

void RunManagement::notifyContentChanged(const QString & path_to_file)
{
    auto runner = _findRunner();
    if (runner == nullptr)
        return;

    if ((runner->plugin()->supports() & shell::PS_Run_AutoPreview) == 0)
        return;

    if (runner->isAutoRunActive())
        runner->startModeling(path_to_file);
}

void RunManagement::started()
{
    notifyAboutStateChanges(shell::RunnerState::Running);
}

void RunManagement::paused()
{
    notifyAboutStateChanges(shell::RunnerState::Paused);
}

void RunManagement::stoped()
{
    _last_runner = nullptr;
    notifyAboutStateChanges(shell::RunnerState::Stoped);
}

void RunManagement::notifyAboutStateChanges(shell::RunnerState state)
{
    _state = state;

    switch (state)
    {
    case shell::RunnerState::Running:
        _main_window.displayModelingState(tr("Running"));
        _main_window.sendGlobal(tr("Modeling started"), shell::MessageSeverity::Warning);
        break;
    case shell::RunnerState::Paused:
        _main_window.displayModelingState(tr("Paused"));
        _main_window.sendGlobal(tr("Modeling paused"), shell::MessageSeverity::Warning);
        break;
    case shell::RunnerState::Stoped:
        _main_window.displayModelingState("");
        _main_window.sendGlobal(tr("Modeling stoped"), shell::MessageSeverity::Warning);
        break;
    default:
        break;
    }

    const QVector<PanelPluginData> & panels = _main_window.panels();
    for(const PanelPluginData & ppd : panels)
        for(const PanelData & pd : ppd.panels) {
        shell::AdaptorModeling_interface * modeling_interface = pd.adaptor->getModelingInterface();
        if (!modeling_interface)
            continue;
        switch (state)
        {
        case shell::RunnerState::Running:
            modeling_interface->started();
            break;
        case shell::RunnerState::Paused:
            modeling_interface->paused();
            break;
        case shell::RunnerState::Stoped:
            modeling_interface->stoped();
            break;
        default:
            break;
        }
    }

    _main_window.updateMenus();
}

shell::Runner_interface * RunManagement::_findRunner()
{
    QComboBox * combo = _main_window.runners_combo();
    if (combo->currentText().isEmpty())
    {
        return nullptr;
    }

    auto & runners = _main_window.runners();
    auto r_it = std::find_if( runners.begin()
                            , runners.end()
                            , [combo](shell::Runner_interface * r) -> bool
                            {
                                return r->name() == combo->currentText();
                            });

    if (r_it == runners.end())
    {
        return nullptr;
    }

    return *r_it;
}

shell::Runner_interface * RunManagement::_findRunnerOrLast()
{
    if (_last_runner != nullptr)
    {
        return _last_runner;
    }

    return _findRunner();
}
